
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import dispatchRequest from './dispatchRequest'
import buildURL from '../helpers/buildURL'
import * as utils from '../utils'

export default class Axios {
    // 配置
    defaults: DefaultsConfigs & AxiosRequestConfig
    // 拦截器,就是一个长链 promise, 一步步执行一步步then
    interceptors: AxiosInterceptors
    constructor(instanceConfig: DefaultsConfigs) {
        this.defaults = instanceConfig
        this.interceptors = {
            request: new InterceptorManager<AxiosRequestConfig>(),
            response: new InterceptorManager<AxiosResponse>()
        };
    }
    request<T = any, R = AxiosResponse<T>>(config: AxiosRequestConfig): Promise<R> {
        let newConfig: AxiosRequestConfig
        if (typeof config === 'string') {
            newConfig = arguments[1] || {};
            newConfig.url = arguments[0];
        } else {
            newConfig = config || {};
        }
        // 合并默认配置和请求配置
        newConfig = mergeConfig(this.defaults, config);
        if (newConfig.method) {
            // 请求方法名都是小写
            newConfig.method = newConfig.method.toLowerCase();
        } else if (this.defaults.method) {
            // 请求方法名都是小写
            newConfig.method = this.defaults.method.toLowerCase();
        } else {
            // 默认get请求
            newConfig.method = 'get';
        }
        // 连接拦截器中间件
        // dispatchRequest 才是真正发请求的
        let chain = [dispatchRequest, undefined];
        let promise: any = Promise.resolve(config);
        // request 拦截器前方插入
        this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
            chain.unshift(interceptor.fulfilled, interceptor.rejected);
        });
        // response 拦截器后方插入
        this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
            chain.push(interceptor.fulfilled, interceptor.rejected);
        });
        // 生成长链 promise, 一步步执行一步步then
        while (chain.length) {
            promise = promise.then(chain.shift(), chain.shift());
        }
        return promise;
    }
    getUri(config: AxiosRequestConfig): string {
        config = mergeConfig(this.defaults, config);
        return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
    }
}

utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
    Axios.prototype[method] = function (url: string, config: any) {
        return this.request(mergeConfig(config || {}, {
            method: method,
            url: url,
            data: (config || {}).data
        }));
    };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
    Axios.prototype[method] = function (url: string, data: any, config: any) {
        return this.request(mergeConfig(config || {}, {
            method: method,
            url: url,
            data: data
        }));
    };
});